{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "41dd56f8-a2cb-4e31-8236-dd9c86decb35",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1 + 2 + 3"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49759e85-1f81-4009-97aa-3e850177fc44",
   "metadata": {},
   "source": [
    "```python\n",
    "def greet(name):\n",
    "    return f\"Hello, {name}!\"\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df839d55-9313-48bd-9631-2aa833d79e0c",
   "metadata": {},
   "source": [
    "<h2>欢迎学习 JupyterLab！</h2>\n",
    "<p>这是一个段落。</p>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01a8e8b2-a61f-4555-8f42-20d05ea7b6a3",
   "metadata": {},
   "source": [
    "这是行内公式：$a^2 + b^2 = c^2$\n",
    "\n",
    "这是整行公式：\n",
    "\n",
    "$$\n",
    "\\int_0^1 x^2 \\, dx = \\frac{1}{3}\n",
    "$$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a69b7c0-6263-47e1-97fa-3785e468ee7a",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c37b9299-359a-4ea8-ab9d-fa5d4dc46c0d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3251ab56-3b53-4812-9d29-960ee0849873",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8e498db7-8a1a-4589-98ca-0d8f246c06ae",
   "metadata": {},
   "outputs": [
    {
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mImportError\u001b[39m                               Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpolars\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpl\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mperspective\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m PerspectiveWidget\n\u001b[32m      4\u001b[39m \u001b[38;5;66;03m# 从本地读取 Parquet 文件\u001b[39;00m\n\u001b[32m      5\u001b[39m df = pl.read_parquet(\u001b[33m\"\u001b[39m\u001b[33mnew_share.parquet\u001b[39m\u001b[33m\"\u001b[39m)\n",
      "\u001b[31mImportError\u001b[39m: cannot import name 'PerspectiveWidget' from 'perspective' (C:\\Users\\liu\\anaconda3\\envs\\week07\\Lib\\site-packages\\perspective\\__init__.py)"
     ]
    }
   ],
   "source": [
    "import polars as pl\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "# 读取 Parquet 文件\n",
    "df = pl.read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# 展示交互式表格\n",
    "PerspectiveWidget(df)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0bd1cb1-b41c-4dac-9a50-58ded42a05d4",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3f943a7c-c489-43ac-b165-6a68085e8afe",
   "metadata": {},
   "outputs": [],
   "source": [
    "import polars as pl\n",
    "\n",
    "# 读取 Parquet，分别命名为 d1（新股数据）\n",
    "d1 = pl.read_parquet(\"new_share.parquet\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "c415e13f-3c14-4d0f-ac7a-2dece20e639b",
   "metadata": {},
   "outputs": [
    {
     "ename": "SchemaError",
     "evalue": "invalid series dtype: expected `String`, got `date` for series with name `issue_date`",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mSchemaError\u001b[39m                               Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpolars\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m col, Date\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m d1 = d1.with_columns([\n\u001b[32m      4\u001b[39m     col(\u001b[33m\"\u001b[39m\u001b[33mipo_date\u001b[39m\u001b[33m\"\u001b[39m).str.strptime(Date, \u001b[33m\"\u001b[39m\u001b[33m%\u001b[39m\u001b[33mY\u001b[39m\u001b[33m%\u001b[39m\u001b[33mm\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[33m\"\u001b[39m).alias(\u001b[33m\"\u001b[39m\u001b[33mipo_date\u001b[39m\u001b[33m\"\u001b[39m),\n\u001b[32m      5\u001b[39m     col(\u001b[33m\"\u001b[39m\u001b[33missue_date\u001b[39m\u001b[33m\"\u001b[39m).str.strptime(Date, \u001b[33m\"\u001b[39m\u001b[33m%\u001b[39m\u001b[33mY\u001b[39m\u001b[33m%\u001b[39m\u001b[33mm\u001b[39m\u001b[38;5;132;01m%d\u001b[39;00m\u001b[33m\"\u001b[39m).alias(\u001b[33m\"\u001b[39m\u001b[33missue_date\u001b[39m\u001b[33m\"\u001b[39m),\n\u001b[32m      6\u001b[39m ])\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\site-packages\\polars\\dataframe\\frame.py:9819\u001b[39m, in \u001b[36mDataFrame.with_columns\u001b[39m\u001b[34m(self, *exprs, **named_exprs)\u001b[39m\n\u001b[32m   9673\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mwith_columns\u001b[39m(\n\u001b[32m   9674\u001b[39m     \u001b[38;5;28mself\u001b[39m,\n\u001b[32m   9675\u001b[39m     *exprs: IntoExpr | Iterable[IntoExpr],\n\u001b[32m   9676\u001b[39m     **named_exprs: IntoExpr,\n\u001b[32m   9677\u001b[39m ) -> DataFrame:\n\u001b[32m   9678\u001b[39m \u001b[38;5;250m    \u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m   9679\u001b[39m \u001b[33;03m    Add columns to this DataFrame.\u001b[39;00m\n\u001b[32m   9680\u001b[39m \n\u001b[32m   (...)\u001b[39m\u001b[32m   9817\u001b[39m \u001b[33;03m    └─────┴──────┴─────────────┘\u001b[39;00m\n\u001b[32m   9818\u001b[39m \u001b[33;03m    \"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m9819\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.lazy().with_columns(*exprs, **named_exprs).collect(_eager=\u001b[38;5;28;01mTrue\u001b[39;00m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\site-packages\\polars\\_utils\\deprecation.py:93\u001b[39m, in \u001b[36mdeprecate_streaming_parameter.<locals>.decorate.<locals>.wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m     89\u001b[39m         kwargs[\u001b[33m\"\u001b[39m\u001b[33mengine\u001b[39m\u001b[33m\"\u001b[39m] = \u001b[33m\"\u001b[39m\u001b[33min-memory\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m     91\u001b[39m     \u001b[38;5;28;01mdel\u001b[39;00m kwargs[\u001b[33m\"\u001b[39m\u001b[33mstreaming\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m---> \u001b[39m\u001b[32m93\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m function(*args, **kwargs)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\site-packages\\polars\\lazyframe\\frame.py:2207\u001b[39m, in \u001b[36mLazyFrame.collect\u001b[39m\u001b[34m(self, type_coercion, _type_check, predicate_pushdown, projection_pushdown, simplify_expression, slice_pushdown, comm_subplan_elim, comm_subexpr_elim, cluster_with_columns, collapse_joins, no_optimization, engine, background, _check_order, _eager, **_kwargs)\u001b[39m\n\u001b[32m   2205\u001b[39m \u001b[38;5;66;03m# Only for testing purposes\u001b[39;00m\n\u001b[32m   2206\u001b[39m callback = _kwargs.get(\u001b[33m\"\u001b[39m\u001b[33mpost_opt_callback\u001b[39m\u001b[33m\"\u001b[39m, callback)\n\u001b[32m-> \u001b[39m\u001b[32m2207\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m wrap_df(ldf.collect(engine, callback))\n",
      "\u001b[31mSchemaError\u001b[39m: invalid series dtype: expected `String`, got `date` for series with name `issue_date`"
     ]
    }
   ],
   "source": [
    "from polars import col, Date\n",
    "\n",
    "d1 = d1.with_columns([\n",
    "    col(\"ipo_date\").str.strptime(Date, \"%Y%m%d\").alias(\"ipo_date\"),\n",
    "    col(\"issue_date\").str.strptime(Date, \"%Y%m%d\").alias(\"issue_date\"),\n",
    "])\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "55e0902a-6945-4106-b841-11e3d5480e3b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Schema([('ts_code', String),\n",
       "        ('sub_code', String),\n",
       "        ('name', String),\n",
       "        ('ipo_date', Date),\n",
       "        ('issue_date', Date),\n",
       "        ('amount', Float64),\n",
       "        ('market_amount', Float64),\n",
       "        ('price', Float64),\n",
       "        ('pe', Float64),\n",
       "        ('limit_amount', Float64),\n",
       "        ('funds', Float64),\n",
       "        ('ballot', Float64)])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import polars as pl\n",
    "from polars import col, Date\n",
    "\n",
    "# 1. 重新读取 Parquet 文件\n",
    "d1 = pl.read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# 2. 一次性把两列由 \"YYYYMMDD\" 字符串转换成 Date\n",
    "d1 = d1.with_columns([\n",
    "    col(\"ipo_date\").str.strptime(Date, \"%Y%m%d\").alias(\"ipo_date\"),\n",
    "    col(\"issue_date\").str.strptime(Date, \"%Y%m%d\").alias(\"issue_date\"),\n",
    "])\n",
    "\n",
    "# 3. 查看 schema，确认类型\n",
    "d1.schema\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "80f054a1-cf47-44b3-8d67-19ea89b91d0b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "52ce22a7e9f54114a236cceac7c00c04",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(d1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "39047f08-7a7a-460d-87b6-5acaec727075",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "52ce22a7e9f54114a236cceac7c00c04",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(d1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ab48b8d9-c98f-45b2-9f31-841f606fba0e",
   "metadata": {},
   "outputs": [
    {
     "ename": "JSONDecodeError",
     "evalue": "Expecting value: line 2 column 1 (char 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mJSONDecodeError\u001b[39m                           Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 5\u001b[39m\n\u001b[32m      2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mperspective\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mwidget\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m PerspectiveWidget\n\u001b[32m      4\u001b[39m \u001b[38;5;66;03m# 如果你直接粘贴了 JSON 字符串，用三引号包裹：\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m5\u001b[39m config = json.loads(\u001b[33mr\u001b[39m\u001b[33m\"\"\"\u001b[39m\n\u001b[32m      6\u001b[39m \u001b[33m粘贴你刚才复制的 JSON 字符串\u001b[39m\n\u001b[32m      7\u001b[39m \u001b[33m\"\"\"\u001b[39m)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\json\\__init__.py:346\u001b[39m, in \u001b[36mloads\u001b[39m\u001b[34m(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)\u001b[39m\n\u001b[32m    341\u001b[39m     s = s.decode(detect_encoding(s), \u001b[33m'\u001b[39m\u001b[33msurrogatepass\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m    343\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\u001b[38;5;28mcls\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m object_hook \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m\n\u001b[32m    344\u001b[39m         parse_int \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m parse_float \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m\n\u001b[32m    345\u001b[39m         parse_constant \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m object_pairs_hook \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m kw):\n\u001b[32m--> \u001b[39m\u001b[32m346\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m _default_decoder.decode(s)\n\u001b[32m    347\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mcls\u001b[39m \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m    348\u001b[39m     \u001b[38;5;28mcls\u001b[39m = JSONDecoder\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\json\\decoder.py:338\u001b[39m, in \u001b[36mJSONDecoder.decode\u001b[39m\u001b[34m(self, s, _w)\u001b[39m\n\u001b[32m    333\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdecode\u001b[39m(\u001b[38;5;28mself\u001b[39m, s, _w=WHITESPACE.match):\n\u001b[32m    334\u001b[39m \u001b[38;5;250m    \u001b[39m\u001b[33;03m\"\"\"Return the Python representation of ``s`` (a ``str`` instance\u001b[39;00m\n\u001b[32m    335\u001b[39m \u001b[33;03m    containing a JSON document).\u001b[39;00m\n\u001b[32m    336\u001b[39m \n\u001b[32m    337\u001b[39m \u001b[33;03m    \"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m338\u001b[39m     obj, end = \u001b[38;5;28mself\u001b[39m.raw_decode(s, idx=_w(s, \u001b[32m0\u001b[39m).end())\n\u001b[32m    339\u001b[39m     end = _w(s, end).end()\n\u001b[32m    340\u001b[39m     \u001b[38;5;28;01mif\u001b[39;00m end != \u001b[38;5;28mlen\u001b[39m(s):\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\json\\decoder.py:356\u001b[39m, in \u001b[36mJSONDecoder.raw_decode\u001b[39m\u001b[34m(self, s, idx)\u001b[39m\n\u001b[32m    354\u001b[39m     obj, end = \u001b[38;5;28mself\u001b[39m.scan_once(s, idx)\n\u001b[32m    355\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mStopIteration\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m--> \u001b[39m\u001b[32m356\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m JSONDecodeError(\u001b[33m\"\u001b[39m\u001b[33mExpecting value\u001b[39m\u001b[33m\"\u001b[39m, s, err.value) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m    357\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m obj, end\n",
      "\u001b[31mJSONDecodeError\u001b[39m: Expecting value: line 2 column 1 (char 1)"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "# 如果你直接粘贴了 JSON 字符串，用三引号包裹：\n",
    "config = json.loads(r\"\"\"\n",
    "粘贴你刚才复制的 JSON 字符串\n",
    "\"\"\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "7cddb8a5-bfcb-40af-90a9-240a76e7c13f",
   "metadata": {},
   "outputs": [
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'config.json'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mFileNotFoundError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mjson\u001b[39;00m\n\u001b[32m      2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpathlib\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Path\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m config = json.loads(Path(\u001b[33m\"\u001b[39m\u001b[33mconfig.json\u001b[39m\u001b[33m\"\u001b[39m).read_text())\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\pathlib.py:1027\u001b[39m, in \u001b[36mPath.read_text\u001b[39m\u001b[34m(self, encoding, errors)\u001b[39m\n\u001b[32m   1023\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m   1024\u001b[39m \u001b[33;03mOpen the file in text mode, read it, and close the file.\u001b[39;00m\n\u001b[32m   1025\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m   1026\u001b[39m encoding = io.text_encoding(encoding)\n\u001b[32m-> \u001b[39m\u001b[32m1027\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m.open(mode=\u001b[33m'\u001b[39m\u001b[33mr\u001b[39m\u001b[33m'\u001b[39m, encoding=encoding, errors=errors) \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[32m   1028\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m f.read()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\pathlib.py:1013\u001b[39m, in \u001b[36mPath.open\u001b[39m\u001b[34m(self, mode, buffering, encoding, errors, newline)\u001b[39m\n\u001b[32m   1011\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m mode:\n\u001b[32m   1012\u001b[39m     encoding = io.text_encoding(encoding)\n\u001b[32m-> \u001b[39m\u001b[32m1013\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m io.open(\u001b[38;5;28mself\u001b[39m, mode, buffering, encoding, errors, newline)\n",
      "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: 'config.json'"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from pathlib import Path\n",
    "config = json.loads(Path(\"config.json\").read_text())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "b8a9b759-ff22-4857-9127-bfc607d7d8b6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n"
     ]
    },
    {
     "ename": "FileNotFoundError",
     "evalue": "[Errno 2] No such file or directory: 'config.json'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mFileNotFoundError\u001b[39m                         Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m      5\u001b[39m \u001b[38;5;28mprint\u001b[39m(Path(\u001b[33m\"\u001b[39m\u001b[33mconfig.json\u001b[39m\u001b[33m\"\u001b[39m).exists())\n\u001b[32m      7\u001b[39m \u001b[38;5;66;03m# 2）读取并解析：\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m config = json.loads(Path(\u001b[33m\"\u001b[39m\u001b[33mconfig.json\u001b[39m\u001b[33m\"\u001b[39m).read_text())\n\u001b[32m      9\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;28mtype\u001b[39m(config), config.keys())\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\pathlib.py:1027\u001b[39m, in \u001b[36mPath.read_text\u001b[39m\u001b[34m(self, encoding, errors)\u001b[39m\n\u001b[32m   1023\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m   1024\u001b[39m \u001b[33;03mOpen the file in text mode, read it, and close the file.\u001b[39;00m\n\u001b[32m   1025\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m   1026\u001b[39m encoding = io.text_encoding(encoding)\n\u001b[32m-> \u001b[39m\u001b[32m1027\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m.open(mode=\u001b[33m'\u001b[39m\u001b[33mr\u001b[39m\u001b[33m'\u001b[39m, encoding=encoding, errors=errors) \u001b[38;5;28;01mas\u001b[39;00m f:\n\u001b[32m   1028\u001b[39m     \u001b[38;5;28;01mreturn\u001b[39;00m f.read()\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\pathlib.py:1013\u001b[39m, in \u001b[36mPath.open\u001b[39m\u001b[34m(self, mode, buffering, encoding, errors, newline)\u001b[39m\n\u001b[32m   1011\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"\u001b[39m\u001b[33mb\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;129;01min\u001b[39;00m mode:\n\u001b[32m   1012\u001b[39m     encoding = io.text_encoding(encoding)\n\u001b[32m-> \u001b[39m\u001b[32m1013\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m io.open(\u001b[38;5;28mself\u001b[39m, mode, buffering, encoding, errors, newline)\n",
      "\u001b[31mFileNotFoundError\u001b[39m: [Errno 2] No such file or directory: 'config.json'"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from pathlib import Path\n",
    "\n",
    "# 1）确认文件存在：\n",
    "print(Path(\"config.json\").exists())\n",
    "\n",
    "# 2）读取并解析：\n",
    "config = json.loads(Path(\"config.json\").read_text())\n",
    "print(type(config), config.keys())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0b19c1f1-1494-4f7a-96f1-937b28427c5f",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'config' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[14]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m      5\u001b[39m d1 = read_parquet(\u001b[33m\"\u001b[39m\u001b[33mnew_share.parquet\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      7\u001b[39m \u001b[38;5;66;03m# 用导出的配置一次性渲染\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m PerspectiveWidget(d1, **config)\n",
      "\u001b[31mNameError\u001b[39m: name 'config' is not defined"
     ]
    }
   ],
   "source": [
    "from polars import read_parquet\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "# 重新读 DataFrame\n",
    "d1 = read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# 用导出的配置一次性渲染\n",
    "PerspectiveWidget(d1, **config)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "71d2af77-be3b-4842-85f9-d473fda7d239",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'config' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mNameError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[15]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m      5\u001b[39m d1 = read_parquet(\u001b[33m\"\u001b[39m\u001b[33mnew_share.parquet\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      7\u001b[39m \u001b[38;5;66;03m# 用导出的配置一次性渲染\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m PerspectiveWidget(d1, **config)\n",
      "\u001b[31mNameError\u001b[39m: name 'config' is not defined"
     ]
    }
   ],
   "source": [
    "from polars import read_parquet\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "# 重新读 DataFrame\n",
    "d1 = read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# 用导出的配置一次性渲染\n",
    "PerspectiveWidget(d1, **config)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4ee14be7-900e-465f-a813-791be98ef382",
   "metadata": {},
   "outputs": [],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "import json\n",
    "\n",
    "# 1）先把 widget 赋给一个变量\n",
    "w = PerspectiveWidget(d1)\n",
    "\n",
    "# 2）在 Notebook 里显示它\n",
    "w\n",
    "\n",
    "# 3）然后直接读取它的 config 属性并格式化打印\n",
    "config = w.config  # 它是一个 dict\n",
    "print(json.dumps(config, indent=2, ensure_ascii=False))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c1436523-2e13-4752-b4b6-2d69170fa94d",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'PerspectiveWidget' object has no attribute 'config'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m      8\u001b[39m w\n\u001b[32m     10\u001b[39m \u001b[38;5;66;03m# 3) 然后直接读取它的 config 属性并格式化打印\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m config = w.config  \u001b[38;5;66;03m# 它是一个 dict\u001b[39;00m\n\u001b[32m     12\u001b[39m \u001b[38;5;28mprint\u001b[39m(json.dumps(config, indent=\u001b[32m2\u001b[39m, ensure_ascii=\u001b[38;5;28;01mFalse\u001b[39;00m))\n",
      "\u001b[31mAttributeError\u001b[39m: 'PerspectiveWidget' object has no attribute 'config'"
     ]
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "import json\n",
    "\n",
    "# 1) 先把 widget 赋给一个变量\n",
    "w = PerspectiveWidget(d1)\n",
    "\n",
    "# 2) 在 Notebook 里显示它\n",
    "w\n",
    "\n",
    "# 3) 然后直接读取它的 config 属性并格式化打印\n",
    "config = w.config  # 它是一个 dict\n",
    "print(json.dumps(config, indent=2, ensure_ascii=False))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "9d3e2e1d-b58f-4d41-be78-ee838fa81702",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"group_by\": [],\n",
      "  \"split_by\": [],\n",
      "  \"filter\": [],\n",
      "  \"sort\": [],\n",
      "  \"aggregates\": {},\n",
      "  \"columns\": [\n",
      "    \"ts_code\",\n",
      "    \"sub_code\",\n",
      "    \"name\",\n",
      "    \"ipo_date\",\n",
      "    \"issue_date\",\n",
      "    \"amount\",\n",
      "    \"market_amount\",\n",
      "    \"price\",\n",
      "    \"pe\",\n",
      "    \"limit_amount\",\n",
      "    \"funds\",\n",
      "    \"ballot\"\n",
      "  ],\n",
      "  \"expressions\": {},\n",
      "  \"plugin\": \"Datagrid\",\n",
      "  \"plugin_config\": {},\n",
      "  \"theme\": null,\n",
      "  \"settings\": true,\n",
      "  \"title\": null,\n",
      "  \"version\": \"3.6.0\"\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "import json, pathlib\n",
    "\n",
    "# 假设 d1 已经是你的 DataFrame\n",
    "w = PerspectiveWidget(d1)   # 把 widget 赋给变量\n",
    "w                           # 显示在输出里，方便你交互调整\n",
    "\n",
    "# 当你在 UI 里拖拽完 Group‑By / Split‑By / Where ……\n",
    "# 调用 save() 拿到当前可视化的配置\n",
    "config = w.save()           # <-- 这里返回一个 dict\n",
    "\n",
    "# 格式化查看\n",
    "print(json.dumps(config, indent=2, ensure_ascii=False))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "1bd2579d-1686-49ac-bd7f-cf5c1f3a83e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "433"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pathlib.Path(\"config.json\").write_text(\n",
    "    json.dumps(config, indent=2, ensure_ascii=False),\n",
    "    encoding=\"utf-8\"\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "923ec74b-d201-4b4c-b405-4588e330ab46",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "22a92c2aaee047a993171abfd1e879bc",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import json, pathlib\n",
    "from polars import read_parquet\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "d1 = read_parquet(\"new_share.parquet\")            # 数据\n",
    "config = json.loads(pathlib.Path(\"config.json\").read_text())\n",
    "\n",
    "PerspectiveWidget(d1, **config)                   # 一步复现\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "e8767214-cbb1-454e-bbf0-51d0a54d8722",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "a45a72c10c6b4442b25223c651c30aff",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'name', 'ipo_date', 'price'], expressions={'max_s…"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 先取到一个默认的 config\n",
    "config = {\n",
    "  \"group_by\":    [], \n",
    "  \"split_by\":    [], \n",
    "  \"filter\":      [], \n",
    "  \"sort\":        [], \n",
    "  \"aggregates\":  {}, \n",
    "  \"columns\":     list(d1.columns),\n",
    "  \"expressions\": {},\n",
    "  \"plugin\":      \"Datagrid\",\n",
    "  \"plugin_config\": {},\n",
    "  \"settings\":    True\n",
    "}\n",
    "\n",
    "# 举例：按 ipo_date 分组\n",
    "config[\"group_by\"] = [\"ipo_date\"]\n",
    "\n",
    "# 举例：按 ts_code 拆分\n",
    "config[\"split_by\"] = [\"ts_code\"]\n",
    "\n",
    "# 举例：只看发行价 price > 20\n",
    "config[\"filter\"] = [[\"price\", \">\", \"20\"]]\n",
    "\n",
    "# 举例：只显示这几列\n",
    "config[\"columns\"] = [\"ts_code\", \"name\", \"ipo_date\", \"price\"]\n",
    "\n",
    "# 举例：添加一个计算列 max_shares = limit_amount / price\n",
    "config[\"expressions\"] = {\n",
    "  \"max_shares\": {\n",
    "    \"column\":   \"limit_amount\",\n",
    "    \"expression\": \"limit_amount / price\"\n",
    "  }\n",
    "}\n",
    "\n",
    "# 然后直接用这份 config 渲染：\n",
    "from perspective.widget import PerspectiveWidget\n",
    "PerspectiveWidget(d1, **config)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "f3635a64-181c-4f30-ae35-4b4eea4b471e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "81bcc78ccd804a5ea224dd6771c9a0eb",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'name', 'ipo_date', 'price', 'limit_amount'], exp…"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from polars import read_parquet\n",
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "# 1. 重新读取你的 DataFrame\n",
    "d1 = read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# 2. 手动构造一个 config 字典：\n",
    "config = {\n",
    "  \"group_by\":       [\"ipo_date\"],       # 按 ipo_date 纵向分组\n",
    "  \"split_by\":       [\"ts_code\"],        # 按 ts_code 横向拆分\n",
    "  \"filter\":         [[\"price\", \">\", 20]],   # 只看 price > 20\n",
    "  \"sort\":           [[\"ipo_date\", \"asc\"]],  # 按 ipo_date 升序排序（可选）\n",
    "  \"aggregates\":     {},                  # 默认数值列用 sum 汇总\n",
    "  \"columns\":        [                    # 要显示的列顺序\n",
    "    \"ts_code\",\"name\",\"ipo_date\",\"price\",\"limit_amount\"\n",
    "  ],\n",
    "  \"expressions\": {                       # 新增计算列\n",
    "    \"max_shares\": {\n",
    "      \"column\":     \"limit_amount\",\n",
    "      \"expression\": \"limit_amount / price\"\n",
    "    }\n",
    "  },\n",
    "  \"plugin\":         \"Datagrid\",\n",
    "  \"plugin_config\":  {},\n",
    "  \"settings\":       True,\n",
    "  \"title\":          None,\n",
    "  \"version\":        \"3.6.0\"\n",
    "}\n",
    "\n",
    "# 3. 一次性渲染\n",
    "PerspectiveWidget(d1, **config)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "08043c70-8049-4460-9608-875f57dc2957",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "dc2583f569b648a09abbb121f63f28f8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin=\"Treemap\",\n",
    "    plugin_config={\n",
    "        # 分类维度\n",
    "        \"row_pivots\": [\"name\"],     \n",
    "\n",
    "        # 矩形“大小”的数据列\n",
    "        \"columns\":    [\"limit_amount\"],\n",
    "\n",
    "        # 矩形“颜色”的数据列\n",
    "        \"color\":      [\"price\"],     \n",
    "\n",
    "        # 聚合方式：数值字段用 sum 汇总\n",
    "        \"aggregates\": {                \n",
    "            \"limit_amount\": \"sum\", \n",
    "            \"price\":        \"avg\"\n",
    "        }\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "03784323-4442-473a-be30-8162c14bffb6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "7ac4161e88ae42fea85709a870232191",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin=\"Y Bar\",\n",
    "    plugin_config={\n",
    "        # 横轴：每个 ts_code\n",
    "        \"row_pivots\": [\"ts_code\"],   \n",
    "\n",
    "        # 纵轴：count 或 sum\n",
    "        \"columns\":    [\"limit_amount\"],\n",
    "\n",
    "        # 聚合方式：limit_amount 求和\n",
    "        \"aggregates\": {\n",
    "            \"limit_amount\": \"sum\"\n",
    "        }\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "ad10ab96-ae66-4d6c-ad2c-13ec1d4d8c73",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'DataFrame' object has no attribute 'groupby'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[24]\u001b[39m\u001b[32m, line 7\u001b[39m\n\u001b[32m      2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpolars\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m col\n\u001b[32m      4\u001b[39m \u001b[38;5;66;03m# 1 Treemap：取 limit_amount 总和最大的前三个 name\u001b[39;00m\n\u001b[32m      5\u001b[39m top3_names = (\n\u001b[32m      6\u001b[39m     d1\n\u001b[32m----> \u001b[39m\u001b[32m7\u001b[39m     .groupby(\u001b[33m\"\u001b[39m\u001b[33mname\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m      8\u001b[39m     .agg(pl.col(\u001b[33m\"\u001b[39m\u001b[33mlimit_amount\u001b[39m\u001b[33m\"\u001b[39m).sum().alias(\u001b[33m\"\u001b[39m\u001b[33mtotal_limit\u001b[39m\u001b[33m\"\u001b[39m))\n\u001b[32m      9\u001b[39m     .sort(\u001b[33m\"\u001b[39m\u001b[33mtotal_limit\u001b[39m\u001b[33m\"\u001b[39m, descending=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m     10\u001b[39m     .head(\u001b[32m3\u001b[39m)\n\u001b[32m     11\u001b[39m )\n\u001b[32m     12\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mTreemap 前三：\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m     13\u001b[39m \u001b[38;5;28mprint\u001b[39m(top3_names)\n",
      "\u001b[31mAttributeError\u001b[39m: 'DataFrame' object has no attribute 'groupby'"
     ]
    }
   ],
   "source": [
    "import polars as pl\n",
    "from polars import col\n",
    "\n",
    "# 1 Treemap：取 limit_amount 总和最大的前三个 name\n",
    "top3_names = (\n",
    "    d1\n",
    "    .groupby(\"name\")\n",
    "    .agg(pl.col(\"limit_amount\").sum().alias(\"total_limit\"))\n",
    "    .sort(\"total_limit\", descending=True)\n",
    "    .head(3)\n",
    ")\n",
    "print(\"Treemap 前三：\")\n",
    "print(top3_names)\n",
    "\n",
    "# 2 Y Bar：取 limit_amount 总和最大的 ts_code\n",
    "top1_ts = (\n",
    "    d1\n",
    "    .groupby(\"ts_code\")\n",
    "    .agg(pl.col(\"limit_amount\").sum().alias(\"total_limit\"))\n",
    "    .sort(\"total_limit\", descending=True)\n",
    "    .head(1)\n",
    ")\n",
    "print(\"\\nY Bar 最优 ts_code：\")\n",
    "print(top1_ts)\n",
    "\n",
    "# 3 直方图：先分箱，再统计每箱数量\n",
    "d2 = d1.with_columns([col(\"pe\").bucket(10).alias(\"pe_bin\")])\n",
    "hist = (\n",
    "    d2\n",
    "    .groupby(\"pe_bin\")\n",
    "    .agg(pl.count().alias(\"count\"))\n",
    "    .sort(\"count\", descending=True)\n",
    "    .head(1)\n",
    ")\n",
    "print(\"\\n直方图最高箱：\")\n",
    "print(hist)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "6bb1e60d-e9b2-4913-a46c-4811b2b33767",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "PerspectiveViewer.__init__() got an unexpected keyword argument 'color'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mTypeError\u001b[39m                                 Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[25]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mperspective\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mwidget\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m PerspectiveWidget\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m PerspectiveWidget(\n\u001b[32m      4\u001b[39m     d1,\n\u001b[32m      5\u001b[39m     plugin=\u001b[33m\"\u001b[39m\u001b[33mTreemap\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m      6\u001b[39m     \u001b[38;5;66;03m# 透视设置必须放最外层\u001b[39;00m\n\u001b[32m      7\u001b[39m     group_by   = [\u001b[33m\"\u001b[39m\u001b[33mname\u001b[39m\u001b[33m\"\u001b[39m],        \u001b[38;5;66;03m# 分类维度\u001b[39;00m\n\u001b[32m      8\u001b[39m     columns    = [\u001b[33m\"\u001b[39m\u001b[33mlimit_amount\u001b[39m\u001b[33m\"\u001b[39m],\u001b[38;5;66;03m# 用 limit_amount 决定矩形面积\u001b[39;00m\n\u001b[32m      9\u001b[39m     color      = [\u001b[33m\"\u001b[39m\u001b[33mprice\u001b[39m\u001b[33m\"\u001b[39m],       \u001b[38;5;66;03m# 用 price 决定颜色深浅\u001b[39;00m\n\u001b[32m     10\u001b[39m     aggregates = {                \u001b[38;5;66;03m# 汇总方式\u001b[39;00m\n\u001b[32m     11\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33mlimit_amount\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33msum\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m     12\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33mprice\u001b[39m\u001b[33m\"\u001b[39m:        \u001b[33m\"\u001b[39m\u001b[33mavg\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m     13\u001b[39m     },\n\u001b[32m     14\u001b[39m     plugin_config = {\n\u001b[32m     15\u001b[39m         \u001b[38;5;66;03m# Treemap 自己的视觉参数（可选）\u001b[39;00m\n\u001b[32m     16\u001b[39m         \u001b[33m\"\u001b[39m\u001b[33mlabel\u001b[39m\u001b[33m\"\u001b[39m: \u001b[33m\"\u001b[39m\u001b[33mname\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m     17\u001b[39m     }\n\u001b[32m     18\u001b[39m )\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\site-packages\\perspective\\widget\\__init__.py:140\u001b[39m, in \u001b[36mPerspectiveWidget.__init__\u001b[39m\u001b[34m(self, data, index, limit, binding_mode, **kwargs)\u001b[39m\n\u001b[32m    124\u001b[39m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mIndex and Limit cannot be set at the same time!\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m    126\u001b[39m \u001b[38;5;66;03m# Parse the dataset we pass in - if it's Pandas, preserve pivots\u001b[39;00m\n\u001b[32m    127\u001b[39m \u001b[38;5;66;03m# if isinstance(data, pandas.DataFrame) or isinstance(data, pandas.Series):\u001b[39;00m\n\u001b[32m    128\u001b[39m \u001b[38;5;66;03m#     data, config = deconstruct_pandas(data)\u001b[39;00m\n\u001b[32m   (...)\u001b[39m\u001b[32m    138\u001b[39m \n\u001b[32m    139\u001b[39m \u001b[38;5;66;03m# Initialize the viewer\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m140\u001b[39m \u001b[38;5;28msuper\u001b[39m(PerspectiveWidget, \u001b[38;5;28mself\u001b[39m).\u001b[34m__init__\u001b[39m(**kwargs)\n\u001b[32m    142\u001b[39m \u001b[38;5;66;03m# Handle messages from the the front end\u001b[39;00m\n\u001b[32m    143\u001b[39m \u001b[38;5;28mself\u001b[39m.on_msg(\u001b[38;5;28mself\u001b[39m.handle_message)\n",
      "\u001b[36mFile \u001b[39m\u001b[32m~\\anaconda3\\envs\\week07\\Lib\\site-packages\\ipywidgets\\widgets\\widget.py:503\u001b[39m, in \u001b[36mWidget.__init__\u001b[39m\u001b[34m(self, **kwargs)\u001b[39m\n\u001b[32m    501\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Public constructor\"\"\"\u001b[39;00m\n\u001b[32m    502\u001b[39m \u001b[38;5;28mself\u001b[39m._model_id = kwargs.pop(\u001b[33m'\u001b[39m\u001b[33mmodel_id\u001b[39m\u001b[33m'\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[32m--> \u001b[39m\u001b[32m503\u001b[39m \u001b[38;5;28msuper\u001b[39m().\u001b[34m__init__\u001b[39m(**kwargs)\n\u001b[32m    505\u001b[39m Widget._call_widget_constructed(\u001b[38;5;28mself\u001b[39m)\n\u001b[32m    506\u001b[39m \u001b[38;5;28mself\u001b[39m.open()\n",
      "\u001b[31mTypeError\u001b[39m: PerspectiveViewer.__init__() got an unexpected keyword argument 'color'"
     ]
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin=\"Treemap\",\n",
    "    # 透视设置必须放最外层\n",
    "    group_by   = [\"name\"],        # 分类维度\n",
    "    columns    = [\"limit_amount\"],# 用 limit_amount 决定矩形面积\n",
    "    color      = [\"price\"],       # 用 price 决定颜色深浅\n",
    "    aggregates = {                # 汇总方式\n",
    "        \"limit_amount\": \"sum\",\n",
    "        \"price\":        \"avg\"\n",
    "    },\n",
    "    plugin_config = {\n",
    "        # Treemap 自己的视觉参数（可选）\n",
    "        \"label\": \"name\"\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "6c2cb838-3812-4d94-bd1d-c2d1b513741c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1d95e09e736649f99616e356405f49fa",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(aggregates={'limit_amount': 'sum'}, binding_mode='server', columns=['limit_amount'], group_b…"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin=\"Y Bar\",\n",
    "    group_by   = [\"ts_code\"],     # 横轴分组\n",
    "    columns    = [\"limit_amount\"],\n",
    "    aggregates = {\n",
    "        \"limit_amount\": \"sum\"\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "d639d09c-d03b-45a6-92c3-e9bc4349e5c1",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Expr' object has no attribute 'bucket'",
     "output_type": "error",
     "traceback": [
      "\u001b[31m---------------------------------------------------------------------------\u001b[39m",
      "\u001b[31mAttributeError\u001b[39m                            Traceback (most recent call last)",
      "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[27]\u001b[39m\u001b[32m, line 4\u001b[39m\n\u001b[32m      1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpolars\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m col\n\u001b[32m      3\u001b[39m d2 = d1.with_columns(\n\u001b[32m----> \u001b[39m\u001b[32m4\u001b[39m     col(\u001b[33m\"\u001b[39m\u001b[33mpe\u001b[39m\u001b[33m\"\u001b[39m).bucket(\u001b[32m10\u001b[39m).alias(\u001b[33m\"\u001b[39m\u001b[33mpe_bin\u001b[39m\u001b[33m\"\u001b[39m)    \u001b[38;5;66;03m# 把 pe 分 10 桶\u001b[39;00m\n\u001b[32m      5\u001b[39m )\n\u001b[32m      7\u001b[39m PerspectiveWidget(\n\u001b[32m      8\u001b[39m     d2,\n\u001b[32m      9\u001b[39m     plugin=\u001b[33m\"\u001b[39m\u001b[33mY Bar\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m   (...)\u001b[39m\u001b[32m     14\u001b[39m     }\n\u001b[32m     15\u001b[39m )\n",
      "\u001b[31mAttributeError\u001b[39m: 'Expr' object has no attribute 'bucket'"
     ]
    }
   ],
   "source": [
    "from polars import col\n",
    "\n",
    "d2 = d1.with_columns(\n",
    "    col(\"pe\").bucket(10).alias(\"pe_bin\")    # 把 pe 分 10 桶\n",
    ")\n",
    "\n",
    "PerspectiveWidget(\n",
    "    d2,\n",
    "    plugin=\"Y Bar\",\n",
    "    group_by   = [\"pe_bin\"],      # 横轴：桶区间\n",
    "    columns    = [\"count\"],       # 纵轴：计数\n",
    "    aggregates = {\n",
    "        \"pe_bin\": \"count\"         # 计数\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "413a707e-2705-4c25-9c1c-09a0e7e83d15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "07ad1e8ead034a12b0e4f0ec6eae7e44",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(aggregates={'funds': 'sum', 'pe': 'avg'}, binding_mode='server', columns=['funds', 'pe'], gr…"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "from polars import read_parquet\n",
    "\n",
    "# 重新读取数据\n",
    "d1 = read_parquet(\"new_share.parquet\")\n",
    "\n",
    "# Y Line：横轴是 ipo_date，纵轴画 funds（融资额）和 pe（市盈率）\n",
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin     = \"Y Line\",\n",
    "    group_by   = [\"ipo_date\"],          # 横轴：上市日期\n",
    "    columns    = [\"funds\", \"pe\"],       # 纵轴：融资额 & 市盈率\n",
    "    aggregates = {                      # 融资额求和、市盈率求平均\n",
    "        \"funds\": \"sum\",\n",
    "        \"pe\":    \"avg\"\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "be1c193a-443d-492f-8117-57658c0436d0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "47e3949107e04849b11a40281613aab8",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(aggregates={'ballot': 'avg'}, binding_mode='server', columns=['ballot'], group_by=['exchange…"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin     = \"Y Line\",\n",
    "    group_by   = [\"exchange\"],          # 横轴：交易所\n",
    "    columns    = [\"ballot\"],            # 纵轴：中签率\n",
    "    aggregates = {\n",
    "        \"ballot\": \"avg\"                 # 中签率取平均\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "cde9157d-7883-4f0e-a75b-83620fa02e82",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "b4c4c9d17b88469284a01d96ac4552bf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin       = \"X/Y Scatter\",\n",
    "    # 不做分组，则每条记录都是一个点\n",
    "    group_by     = [],\n",
    "    # 哪个字段做 X/Y\n",
    "    plugin_config = {\n",
    "        \"x\": [\"funds\"],\n",
    "        \"y\": [\"pe\"]\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "16ca33dd-52e3-4d9c-8642-ed594826fcfa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "72926b144e8149369b85e3b3b20f3932",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(aggregates={'funds': 'sum', 'pe': 'avg'}, binding_mode='server', columns=['ts_code', 'sub_co…"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin       = \"X/Y Scatter\",\n",
    "    group_by     = [\"industry\"],         # 每个行业一个点\n",
    "    aggregates   = {\n",
    "        \"funds\": \"sum\",                 # 资金求和\n",
    "        \"pe\":    \"avg\"                  # pe 求平均\n",
    "    },\n",
    "    plugin_config = {\n",
    "        \"x\": [\"funds\"],\n",
    "        \"y\": [\"pe\"]\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "fe5f0944-65b8-4599-a660-9423d9b43d8e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ae130a5ecfd34264b3b440be5ea7f5da",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "PerspectiveWidget(binding_mode='server', columns=['ts_code', 'sub_code', 'name', 'ipo_date', 'issue_date', 'am…"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from perspective.widget import PerspectiveWidget\n",
    "\n",
    "PerspectiveWidget(\n",
    "    d1,\n",
    "    plugin       = \"X/Y Scatter\",\n",
    "    group_by     = [\"sub_code\"],      # ← 用 sub_code（或 ts_code）做分组，每组只有一行\n",
    "    plugin_config = {\n",
    "        \"x\": [\"funds\"],               # X 轴：funds\n",
    "        \"y\": [\"pe\"]                   # Y 轴：pe\n",
    "    }\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7ba6a2fd-fa2f-493b-befd-a8c9d6ab3138",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
